#include <iostream>

#include "Log.hpp"

#include "Databank.hpp"

std::map<std::string, ItemData*> Databank::ItemBank;
std::map<unsigned int, RaceData> Databank::Race;

class CustomParserListener : public ITCODParserListener
{
	virtual bool parserNewStruct(TCODParser* parser, const TCODParserStruct* str, const char* name) = 0;
	virtual bool parserFlag(TCODParser* parser, const char* name) = 0;
	virtual bool parserProperty(TCODParser* parser, const char* name, TCOD_value_type_t type, TCOD_value_t value) = 0;
	virtual bool parserEndStruct(TCODParser *parser,const TCODParserStruct *str,const char *name) = 0;
	virtual void error(const char* msg)
	{
		fprintf(stderr, "DATABANK: %s", msg);
		std::cout<<std::endl<<"Press any key to continue..."<<std::endl;;
		std::cin.get();
		exit(1);
	}

public:
	CustomParserListener() : ITCODParserListener() { }
};

class RaceParserListener : public CustomParserListener
{
	bool parserNewStruct(TCODParser* parser, const TCODParserStruct* str, const char* name)
	{
		RaceData data;
		data.Name = name;
		Databank::Race[Databank::Race.size()] = data;

		return true;
	}
	
	bool parserFlag(TCODParser* parser, const char* name)
	{
		return true;
	}

	bool parserProperty(TCODParser* parser, const char* name, TCOD_value_type_t type, TCOD_value_t value)
	{
		unsigned int current = Databank::Race.size()-1;
		std::string sname = name;

		if (sname.compare("key") == 0)
			Databank::Race[current].Key = value.c;
		else if (sname.compare("shape") == 0)
			Databank::Race[current].Shape = value.c;
		else if (sname.compare("size") == 0)
			Databank::Race[current].Size = (EObjectSize)value.i;
		else if (sname.compare("speed") == 0)
			Databank::Race[current].Speed = value.i;
		else if (sname.compare("sight") == 0)
			Databank::Race[current].Sight = value.i;
		else if (sname.compare("hitDice") == 0)
			Databank::Race[current].HitDice = value.dice;
		else if (sname.compare("unarmedDice") == 0)
			Databank::Race[current].UnarmedDice = value.dice;

		return true;
	}

	bool parserEndStruct(TCODParser* parser, const TCODParserStruct* str, const char* name)
	{
		return true;
	}

public:
	RaceParserListener() : CustomParserListener() { } 
};

class ItemParserListener : public CustomParserListener
{
	ItemData* Current;

	bool parserNewStruct(TCODParser* parser, const TCODParserStruct* str, const char* name)
	{
		Current = new ItemData();
		Current->Name = name;

		return true;
	}
	
	bool parserFlag(TCODParser* parser, const char* name)
	{
		std::string sname = name;

		return true;
	}

	bool parserProperty(TCODParser* parser, const char* name, TCOD_value_type_t type, TCOD_value_t value)
	{
		std::string sname = name;

		if (sname.compare("shape") == 0)
			Current->Shape = value.c;
		else if (sname.compare("size") == 0)
			Current->Size = (EObjectSize)value.i;
		else if (sname.compare("transparent") == 0)
			Current->Transparent = value.b;
		else if (sname.compare("foreground") == 0)
			Current->ForeCol = value.col;
		else if (sname.compare("background") == 0)
			Current->BackCol = value.col;
		else if (sname.compare("possibleMaterials") == 0)
			for (int i = 0; i < TCOD_list_size(value.list); ++i)
				Current->PossibleMaterials.push_back((char*)TCOD_list_get(value.list, i));

		return true;
	}

	bool parserEndStruct(TCODParser* parser, const TCODParserStruct* str, const char* name)
	{
		Databank::ItemBank[Current->Name] = new ItemData(*Current);

		delete Current;
		Current = NULL;
		return true;
	}

public:
	ItemParserListener() : CustomParserListener() { } 
}; 

unsigned int Databank::getRaceByName(std::string name)
{
	for (std::map<unsigned int, RaceData>::iterator itr = Race.begin(); itr != Race.end(); ++itr)
		if (name.compare((*itr).second.Name) == 0)
			return (*itr).first;

	return 0;
}

void Databank::clear()
{
	Log::write("Databank: Clearing...");

	ItemBank.clear();
	Race.clear();
}

void Databank::load()
{
	LoadItems();
	LoadRaces();
}

void Databank::LoadItems()
{
	Log::write("Databank: Loading items...");

	TCODParser parser;
	TCODParserStruct* str = parser.newStructure("object");
	str->addProperty("shape", TCOD_TYPE_CHAR, false);
	str->addProperty("size", TCOD_TYPE_INT, false);
	str->addProperty("transparent", TCOD_TYPE_BOOL, false);
	str->addProperty("foreground", TCOD_TYPE_COLOR, false);
	str->addProperty("background", TCOD_TYPE_COLOR, false);
	str->addListProperty("possibleMaterials", TCOD_TYPE_STRING, false);

	ItemParserListener* listener = new ItemParserListener();
	parser.run("data/items", listener);

	int i = ItemBank.size();

	Log::write("Databank: Items loaded.");
}

void Databank::LoadRaces()
{
	Log::write("Databank: Loading races...");

	TCODParser parser;
	TCODParserStruct* str = parser.newStructure("race");
	str->addProperty("key", TCOD_TYPE_CHAR, true);
	str->addProperty("shape", TCOD_TYPE_CHAR, true);
	str->addProperty("size", TCOD_TYPE_INT, true);
	str->addProperty("speed", TCOD_TYPE_INT, true);
	str->addProperty("sight", TCOD_TYPE_INT, true);
	str->addProperty("hitDice", TCOD_TYPE_DICE, true);
	str->addProperty("unarmedDice", TCOD_TYPE_DICE, true);
	
	RaceParserListener* listener = new RaceParserListener();
	parser.run("data/races", listener);

	delete listener;

	Log::write("Databank: Races loaded.");
}
